# -*- coding: UTF-8 -*-
'''
This module implements the harvesting of links and fallback metadata from the tvshack.cc website 

Created on Jan 24, 2010

@author: pguedes
'''
import logging
from metadata.metadata import Metadata
from utils.pluginsupport import encode
from plugin import PluginMovieItem, PluginResult
import linkresolvers
import metadata.metadata as metadatautils
import utils.htmlutils as http
import xbmc, xbmcgui #@UnresolvedImport
import urllib, re
from utils import settings
import plugin

log = logging.getLogger("tvshack")

MODE_LIST_CATEGORY = 'category' 
MODE_PLAY_ITEM = 'play'
MODE_LIST_EPISODES = 'episodes'
MODE_SEARCH = 'search'
MODE_RSS = 'rss'

ALPHA_FILTER="#"

plugin.normalFlowActions.append("selectSource")

@plugin.root()
def listCategories():
  '''List the root categories for this plugin
  @return: a list of PluginMovieItems with the root categories'''
  return PluginResult(7, [ TVShackPluginMovieItem("Tv", 'http://tvshack.cc/tv', MODE_LIST_CATEGORY ),
    TVShackPluginMovieItem("Movies", 'http://tvshack.cc/movies', MODE_LIST_CATEGORY ),
    TVShackPluginMovieItem("Anime", 'http://tvshack.cc/anime', MODE_LIST_CATEGORY ),
    TVShackPluginMovieItem("Music", 'http://tvshack.cc/music', MODE_LIST_CATEGORY ),
    TVShackPluginMovieItem("Documentaries", 'http://tvshack.cc/documentaries', MODE_LIST_CATEGORY ),
    TVShackPluginMovieItem("Updates", '', MODE_RSS),
    TVShackPluginMovieItem("Search", '', MODE_SEARCH)])
  
def categoryContentType(params):
  url = params['url']
  if url.find('/tv/') >= 0:
    return 'tvshows'
  elif url.find('/music') >= 0:
    return 'artists'
  else:
    return 'movies'
  
def _listLetters(url, name):
  # one item for all non-alphabetical titles
  items = [TVShackPluginMovieItem(ALPHA_FILTER, url, MODE_LIST_CATEGORY, extraArgs={"letter": ALPHA_FILTER})]
  # one item per letter in the alphabet
  from string import ascii_uppercase
  for letter in ascii_uppercase:
    items.append(TVShackPluginMovieItem(letter, url, MODE_LIST_CATEGORY, extraArgs={"letter": letter}))
  return PluginResult(len(items), items)

@plugin.mode(MODE_LIST_CATEGORY, categoryContentType)
def listCategory(url, name, letter=None):
  if settings.isSet("group-categories-by-letter") and not letter:
    return _listLetters(url, name)
  # delegate to listCategoryLetter with no filter, to list all
  return _listCategory(url, name, letter)

def _listCategory(url, name, letter=None):
  '''List all the TVShows/Movies/... available
  @param url: url of the title being refreshed
  @param name: the name of the title being refreshed (used for querying)
  @param letter: letter to filter on '#' for non-letter
  @return: a list of PluginMovieItemss'''
  def _shouldInclude(name):
    if not letter or letter == ALPHA_FILTER and not name[0].isalpha():
      return True
    return letter == name[0]
  
  page = http.get(url, cleanup=True)
  #log.debug(page)
  match = re.compile('<li><a href="(.+?)">(.+?)<span').findall(page)
  #log.debug("Results: %s"%str(match))
  if letter:
    match = [item for item in match if _shouldInclude(item[1])]
  size = len(match)
  def itemGenerator():
    for url, name in match:
      # --------------------------------------------------------------
      # For some strange reason, when this code is not here, the loop will fail
      # There seems to be something in the url or name that breaks it.
      try:
        if name:
          log.debug("name size is %s" % str(len(name)))
          log.debug("name is %s" % str(name))
        else:
          log.debug("no name!!!!")
      except:
        log.exception("Fail!")
  
      tags = ""
      if name.find('<font class="new-updated">Updated!</font>') > 0:
        name = name.replace('<font class="new-updated">Updated!</font>', '')
        tags = '[ Updated! ]'
      elif name.find('<font class="new-new">New!</font>') > 0:
        name = name.replace('<font class="new-new">New!</font>', '')
        tags = '[ New! ]'
          
      metadata = metadatautils.get(url, title=name)
      yield TVShackPluginMovieItem(name, url, _getMode(url), metadata, tags)
      
  return PluginResult(size, itemGenerator)
                
@plugin.mode(MODE_LIST_EPISODES, contentType='episodes')
def listEpisodes(url, name):
  '''List the episodes for a TV Show
  @param url: the tvshack url to load episodes from
  @param name: the tvshow being listed
  @return: a list of TVShackPluginMovieItem with the episode items'''
  html = http.get(url, cleanup=True)
  fallbackcover = _harvestCoverArt(html)
  fallbackplot = _harvestSummary(html)
  tvshowMetadata = metadatautils.get(url, fallbackplot, fallbackcover, True, title=name, tvshow=True, persistUpdates=False)
  log.info("tvshow details: %s" % str(tvshowMetadata))
  fallbackMetadata = Metadata(name, fallbackplot, fallbackcover).tvshow(name)

  episodeLinks = re.compile('<li><a href="(.+?)">(.+?)</a><a href=""><span>.+?</span></a>').findall(html)
  if len(episodeLinks) < 1:
    episodeLinks = re.compile('<a href="(.+?)">(.+?)<span>').findall(html)
  itemCount = len(episodeLinks)
  def itemGen():
    item = None
    episodeurl = None
    for episodeurl, episode in episodeLinks:
      try:
        try:
          episodeMetadata = metadatautils.get(episodeurl, fallbackplot, fallbackcover, True, title=episode, tvshow=tvshowMetadata, persistUpdates=False)
          #log.debug("Episode meta: %s" % str(episodeMetadata))
          title = episodeMetadata.getSeasonedTitle()
          item = TVShackPluginMovieItem(title, episodeurl, MODE_PLAY_ITEM, episodeMetadata)
          log.debug("Added item with title: %s" % title)
        except:
          log.exception("Failed to load episode data: %s" % str(episode))
          fallbackMetadata.title(episode)
          item = TVShackPluginMovieItem(episode, episodeurl, MODE_PLAY_ITEM, fallbackMetadata)
      finally:
        yield item
        
    if episodeurl:
      metadatautils.flushCache(episodeurl)
      
  return PluginResult(itemCount, itemGen)
        
@plugin.mode(MODE_PLAY_ITEM, playable=True) 
def resolveFiles(url, name, forceSourceSelection=False):
  '''Resolve the files for a movie/episode/... item

  Resolving a file is split into three phases:
  1-Select file source (megavideo, veoh, tudou, youku, ...)
  2-Load parts (if item is split for the selected source)
  3-Resolve file links for the parts from the previous step
  
  If an item has only one available source, then that source is auto-selected, otherwise
  the user is shown a XBMC dialog to select the source. If the 'autoplay-preferred-source'
  setting is enabled, the first available source that matches the 'preferred-source' setting
  will be auto-selected.
  
  @param url: the url to resolve files for
  @param name: the name of the playable item being resolved
  @param forceSourceSelection: if the user should be forced to select the source (default False)
  @return: a list of urls of the files to play'''
  log.debug("Listing sources: %s, forceSelection: %s" %(url, forceSourceSelection))
  html = http.get(url, cleanup=True)
  alt = re.compile('<h3>Alternate links</h3>.(.+?)</ul>', re.DOTALL).findall(html)
  alternateLinks = re.compile('<li><a href="(.+?)"><img src=".+?logos\/(.+?)\.gif" />').findall(str(alt))
  # check if the source has only one part and if so don't show popup...
  sources = {}
  filterUnsupported = settings.isSet("filter-unsupported-sources")
  for url, source in alternateLinks:
    if linkresolvers.isSupported(source) or not filterUnsupported:
      if not sources.has_key(source):
        sources[source] = []
      sources[source].append(url)
  
  # if we need to resolve manually, we need individual links as choices
  if forceSourceSelection:
    flattenedSources = {}
    for source, urls in sources.iteritems():
      for url in urls:
        flattenedSources["%s#%d" % (source, urls.index(url))] = [url]
    sources = flattenedSources
    
  log.debug("Found sources: %r"%sources)
  sourceNames = sources.keys()
  selected = _selectSource(sourceNames, forceSourceSelection)
  if selected >= 0:
    from linkresolvers import Source
    selSource = sourceNames[selected]
    metadata = metadatautils.get(sources[selSource][0], title=name)
    source = Source(sources[selSource], selSource, metadata)
    return PluginResult(-1, source.resolve())

@plugin.action('loadInfo')
def loadInfo(url, name):
  if _loadInfo(url, name):
    log.debug("Auto-reloading after metadata update")
    xbmc.executebuiltin("Container.Refresh")
  else:
    xbmcgui.Dialog().ok("Load Info...", "Failed!", "Could not load metadata for item: '%s'" % name)  

@plugin.mode(MODE_RSS)
def viewUpdates():
  '''Load RSS feed with most recent updates
  @deprecated: this is currently not used, because I don't!'''
  rssStreams = {"TV":           "http://tvshack.cc/rss/television.xml",
                "Movies":       "http://tvshack.cc/rss/movies.xml",
                "Documentaries":"http://tvshack.cc/rss/documentaries.xml",
                "Music":        "http://tvshack.cc/rss/music.xml",
                "Anime":        "http://tvshack.cc/rss/anime.xml"}
  selected = xbmcgui.Dialog().select('Select update stream', rssStreams.keys())
  log.debug("Selected update stream: %r"%rssStreams.values()[selected])
  link = http.get(rssStreams.values()[selected])
  log.debug("Update stream: %r"%link)
  rss = re.compile('<item>\n<title>(.+?)</title>\n<link>(.+?)</link>').findall(link)
  def itemGen():
    for name, url in rss:
      mode = _getMode(url)
      metadata = metadatautils.get(url)
      yield TVShackPluginMovieItem(name, url, mode, metadata)
  return PluginResult(len(rss), itemGen)

@plugin.mode(MODE_SEARCH)
def search():
  '''Do an interactive search of tvshack.cc's files
  @return: list of files matching search criteria'''
  items = []
  keyb = xbmc.Keyboard('', 'Search TV Shack')
  keyb.doModal()
  if (keyb.isConfirmed()):
    search = keyb.getText()
    encode = urllib.quote(search)
    html = http.get('http://tvshack.cc/search/' + encode, cleanup=True)
    match = re.compile('<li><a href="(.+?)">.+?<strong>(.+?)</strong></a>').findall(html)
    def itemGen():
      for url, name in match:
        mode = _getMode(url)
        metadata = metadatautils.get(url)
        yield TVShackPluginMovieItem(name, url, mode, metadata)
    items = itemGen
  return PluginResult(len(match), items)

class TVShackPluginMovieItem(PluginMovieItem):
  def __init__(self, name, url, mode=None, metadata=None, tags="", extraArgs=None):
    """Create an item 
    @param name: the name for this item
    @param url: the url to load this item
    @param metadata: the metadata to show this item in XBMC
    @param tags: the tags to show """
    PluginMovieItem.__init__(self, name, url, mode, metadata, extraArgs)
    self.title = name + tags
    
  def getTitle(self):
    return self.title
  
def _selectSource(sources, forceSourceSelection):
  if not sources:
    return -1
  elif len(sources) == 1:
    return 0
  else:
    if settings.isSet("autoplay-preferred-source") and not forceSourceSelection:
      autoselect = settings.get("preferred-source")
      for index in range(len(sources)):
        if sources[index] == autoselect:
          return index
     
    return xbmcgui.Dialog().select('Select source', sources)
 
def _getMode(url):
  '''Get the execution mode for this plugin
  The execution mode is used by the plugin to track the type of url being used
  @param url: the url to get the execution mode for'''
  mode = MODE_LIST_EPISODES
  if url.find('/movies/') >= 0 or url.find('/documentaries/') >= 0 or url.find('/comedy/') >= 0:
    mode = MODE_PLAY_ITEM
  return mode
  
def _harvestCoverArt(html):
  '''Read the cover from a TVShack tvshow/movie/documentary page
  @param html: the page's html to parse from
  @return: the cover from tvshack or "" if not found'''
  thumb = re.compile('<img src="(.+?)" alt="Poster" />').findall(html)
  log.debug("Found thumb: %s" % thumb)
  if len(thumb) == 1:
    return thumb[0]
  return ""

def _harvestSummary(html):
  '''Read the summary from a TVShack tvshow/movie/documentary page
  @param html: the page's html to parse from
  @return: the summary from tvshack or "" if not found'''
  summary = re.compile('<h3>Summary</h3>.+?<div>(.+?)</div>', re.DOTALL).findall(html)
  log.debug("Found summary: %s" % summary)
  if len(summary) == 1:
    return summary[0]
  return ""

def _loadInfo(url, name, edit=False):
  '''Force a refresh of the metadata information for an item.
  @param url: url of the title being refreshed
  @param name: the name of the title being refreshed (used for querying)
  @return: true if the metadata was refreshed with online db data, false otherwise'''
  log.info("Loading info for url '%s' of type '%s'" % (url, type))
  search = None
  if edit:
    keyb = xbmc.Keyboard(name, 'Edit title to search')
    keyb.doModal()
    if (keyb.isConfirmed()):
      search = keyb.getText()
  else:
    search = name
  if search:
    meta = metadatautils.get(url, queryOnlineDb=True, bypassCache=True, title=search)
    if not meta.hasCode() and edit == False:
      return _loadInfo(url, search, True)
    return meta.hasCode()
